# 从tushare上获取数据

# coding=utf-8

import tushare as ts
import time
import datetime
from .global_variable import *
from . import models, getBeiJingTime
from django.conf import settings
import sqlite3
from django.db.models import Max
import pandas as pd

#################################################
# 必须加上下面这段，没有token将无法连接tushare
#################################################
try:
    tspro = ts.pro_api(TUSHARE_TOKEN)  # tushare pro 需要在初始化时加上token代码，网址https://tushare.pro
except:
    print('请检查计算机是否正常联网，无法连接tushare平台,数据更新将受到影响！！！')
    # TODO:需要维护，当网络不通时，如何处理异常

#################################################
# 设定的默认常量，NOWTIME不能使用常量，否则只会成为服务启动时的时间，因为服务启动时加载一次后，将不再加载
#################################################
_STARTTIME = '20200101'  # 初始获取数据日期
_NOWTIME = time.strftime('%Y%m%d', time.localtime(time.time()))  # 默认系统当前日期
"""
获取北京时间，如果获取不成功，则使用当地时间
"""


def get_nowtime():
    # 获取北京时间代码，原则上不需要，获取系统默认时间即可
    # BEIJINGTIME = getBeiJingTime.getBeijinTime()
    # if (BEIJINGTIME is None):
    #     _NOWTIME = datetime.datetime.now().strftime('%Y%m%d')
    # else:
    #     _NOWTIME = BEIJINGTIME
    NOWTIME = datetime.datetime.now().strftime('%Y%m%d')
    return NOWTIME


# 新股票基本信息并存入数据库
def update_stockbasic():
    try:
        stock_basic = tspro.stock_basic(
            # list_status='L'
        )  # 默认获取上市股票基本信息，包含七个内容 ts_code，symbol,name,area,industry,market,list_date
    except:

        return '获取网络数据失败，请检查网络是否连接'

    if len(stock_basic.values) == 0: return '无股票基本信息'  # 无数据则推出函数
    try:
        models.stock_basic.objects.all().delete()  # 删除表中数据失败则退出函数，避免重复数据
        # 数据库
        try:
            engine = settings.DATABASES['default']['NAME']
            cn = sqlite3.connect(engine)
        except:
            print('创建数据连接失败')
            return
        stock_basic.to_sql(STOCK_BASIC_TABLE, cn, index=True, index_label='id', if_exists='append')
        # print('更新股票基本信息表成功，目前上市股票有%s个股' % (len(stock_basic.values)))
        return ('更新股票基本信息表成功，目前交易股票有%s个股' % (len(stock_basic.values)))
    except:
        print('更新股票基本信息表失败，请检查数据库后重新更新')
        return '更新股票基本信息表失败，请检查数据库后重新更新'


# 更新公司信息表
def update_company_info():
    try:
        sse = tspro.stock_company(exchange='SSE')  # 获取上证公司信息
        szse = tspro.stock_company(exchange='SZSE')  # 获取深证公司信息
    except:
        return '获取网络数据失败，请检查网络是否连接'

    if (len(sse.values) == 0) or (len(szse.values) == 0): return '公司信息暂无更新'  # 无数据则推出函数

    try:
        engine = settings.DATABASES['default']['NAME']
        cn = sqlite3.connect(engine)
    except:
        print('创建数据连接失败')
        return

    # 清楚数据，并使id重新排列
    models.stock_company.objects.all().delete()  # 删除所有数据
    sql = 'delete from sqlite_sequence where name =\'%s\'' % COMPANY_TABLE  # 清除索引 raw不允许使用字符串拼接，只能用参数
    print(sql)
    models.stock_company.objects.raw(sql)
    try:
        sse.to_sql(COMPANY_TABLE, cn, index=True, index_label='id', if_exists='append')
        # print('更新上市公司信息表成功，目前上市公司有%s家' % (len(sse.values) + len(szse.values)))
    except:
        return ('更新上市公司信息表失败，请检查数据库后重新更新')
    try:
        szse.to_sql(COMPANY_TABLE, cn, index=False, if_exists='append')  # 此处index不添加，默认根据sse的index自增
    except:
        return ('更新深证失败')
    return ('更新上市公司信息表成功，目前上市公司有%s家' % (len(sse.values) + len(szse.values)))


# 将20190101格式的字符串转换成日期%Y-%m-%d返回,默认返回当前日期
def _str_time_to_time(strTime=''):
    nowtmp = time.localtime(time.time())
    nowtmp = datetime.date(nowtmp.tm_year, nowtmp.tm_mon, nowtmp.tm_mday)
    if strTime == '':  return nowtmp
    tmp = time.strptime(strTime, '%Y%m%d')
    tmp = datetime.date(tmp.tm_year, tmp.tm_mon, tmp.tm_mday)
    return tmp


# 获取所有股票的指定日期交易信息
def _get_one_day_stock_daily_info(endtimetmp, cn):
    try:
        df = tspro.daily(trade_date=endtimetmp)
    except:
        return ('获取网络数据失败，请检查网络是否连接')

    if len(df.values) == 0: return endtimetmp + '股票交易信息数据暂未更新'  # 无数据则推出函数

    try:
        df.to_sql(DAILY_TABLE, cn, index=False, if_exists='append')
        return ('插入' + str(endtimetmp) + '股票交易信息共%s条' % (len(df.values)))
    except:
        return ('插入' + str(endtimetmp) + '股票交易信息失败,请检查数据库是否开启！')


# T更新所有股票交易信息,默认获取到当前日期，也可指定更新到某天
def get_all_stock_daily_info():
    starttime = models.daily.objects.all().aggregate(Max('trade_date'))
    starttime = starttime['trade_date__max']
    # 如果未获取最大日期值，默认为_STARTTIME
    if (starttime is None): starttime = _STARTTIME
    _NOWTIME = get_nowtime()  # 需要重新获取本地时间

    if starttime >= _NOWTIME:
        return '股票交易信息更新至' + starttime + '\n'
    try:
        engine = settings.DATABASES['default']['NAME']
        cn = sqlite3.connect(engine)
    except:
        print('创建数据连接失败')
        return

    # 需要将20190101格式字符串转换为时间格式，便于循环
    begin = _str_time_to_time(starttime)  # 转换成日期格式%Y-%m-%d
    end = _str_time_to_time(_NOWTIME)
    labelstr = ''
    for i in range(1, (end - begin).days + 1):  # 按日期循环
        day = begin + datetime.timedelta(days=i)
        if (datetime.date.isoweekday(day) == 6) or (datetime.date.isoweekday(day) == 7): continue  # 周末数据不加载
        day = str(day).replace('-', '')
        labelstr += _get_one_day_stock_daily_info(day, cn) + '\n'
        time.sleep(60 / 200)  # tushare对每秒连接次数有要求，避免网站断开连接
    return labelstr


# 获取指定日期大宗交易交易明细
def _get_one_day_block_trade_info(daytime, cn):
    try:
        df = tspro.block_trade(trade_date=daytime)
    except:
        return ('获取网络数据失败，请检查网络是否连接')
    if len(df.values) == 0: return daytime + '大宗交易数据暂未更新'  # 无数据则推出函数

    try:
        df.to_sql(BLOCK_TRADE_TABLE, cn, index=False, if_exists='append')
        return ('插入' + str(daytime) + '大宗交易共%s条' % (len(df.values)))
    except:
        return ('插入' + str(daytime) + '大宗交易失败,请检查数据库是否开启！')


def get_all_block_trade_info():
    starttime = models.block_trade.objects.all().aggregate(Max('trade_date'))
    starttime = starttime['trade_date__max']
    # 如果未获取最大日期值，默认为_STARTTIME
    if (starttime is None): starttime = _STARTTIME
    _NOWTIME = get_nowtime()  # 需要重新获取本地时间

    if starttime >= _NOWTIME:
        return ('大宗交易当前信息更新至' + starttime + '\n')
    try:
        engine = settings.DATABASES['default']['NAME']
        cn = sqlite3.connect(engine)
    except:
        print('创建数据连接失败')
        return

    # 需要将20190101格式字符串转换为时间格式，便于循环
    begin = _str_time_to_time(starttime)  # 转换成日期格式%Y-%m-%d
    end = _str_time_to_time(_NOWTIME)
    labelstr = ''
    for i in range(1, (end - begin).days + 1):  # 按日期循环
        day = begin + datetime.timedelta(days=i)
        if (datetime.date.isoweekday(day) == 6) or (datetime.date.isoweekday(day) == 7): continue  # 周末数据不加载
        day = str(day).replace('-', '')
        labelstr += _get_one_day_block_trade_info(day, cn) + '\n'
        time.sleep(60 / 200)
    return labelstr


# 获取所有股票的指定日期每日指标信息
def _get_one_day_stock_daily_basic_info(endtimetmp, cn):
    try:
        df = tspro.daily_basic(ts_code='', trade_date=endtimetmp)
    except:
        return ('获取网络数据失败，请检查网络是否连接')

    if len(df.values) == 0:
        return endtimetmp + '每日指标信息数据暂未更新'  # 无数据则推出函数
    try:
        df.to_sql(DAILY_BASIC_TABLE, cn, index=False, if_exists='append')
        return ('插入' + str(endtimetmp) + '每日指标信息共%s条' % (len(df.values)))
    except:
        return ('插入' + str(endtimetmp) + '每日指标信息失败,请检查数据库是否开启！')


# 更新所有股票每日指标信息,默认获取到当前日期，也可指定更新到某天
def get_all_stock_daily_basic_info():
    starttime = models.daily_basic.objects.all().aggregate(Max('trade_date'))
    starttime = starttime['trade_date__max']
    # 如果未获取最大日期值，默认为_STARTTIME
    if (starttime is None): starttime = _STARTTIME
    _NOWTIME = get_nowtime()  # 需要重新获取本地时间

    if starttime >= _NOWTIME:
        return ('每日指标信息信息更新至' + starttime + '\n')

    try:
        engine = settings.DATABASES['default']['NAME']
        cn = sqlite3.connect(engine)
    except:
        print('创建数据连接失败')
        return

    begin = _str_time_to_time(starttime)  # 转换成日期格式%Y-%m-%d
    end = _str_time_to_time(_NOWTIME)
    labelstr = ''
    for i in range(1, (end - begin).days + 1):  # 按日期循环
        day = begin + datetime.timedelta(days=i)
        if (datetime.date.isoweekday(day) == 6) or (datetime.date.isoweekday(day) == 7): continue  # 周末数据不加载
        day = str(day).replace('-', '')
        labelstr += _get_one_day_stock_daily_basic_info(day, cn) + '\n'
        time.sleep(60 / 200)
    return labelstr


# 获取某天个股资金流向
def _get_one_day_moneyflow_info(endtimetmp, cn):
    try:
        df = tspro.moneyflow(trade_date=endtimetmp)
    except:
        return ('获取网络数据失败，请检查网络是否连接')

    if len(df.values) == 0:
        return endtimetmp + '个股资金流向数据暂未更新'  # 无数据则推出函数
    try:
        df.to_sql(MONEYFLOW_TABLE, cn, index=False, if_exists='append')
        return ('插入' + str(endtimetmp) + '个股资金流向信息共%s条' % (len(df.values)))
    except:
        return ('插入' + str(endtimetmp) + '个股资金流向信息失败,请检查数据库是否开启！')


def get_all_moneyflow_info():
    starttime = models.moneyflow.objects.all().aggregate(Max('trade_date'))
    starttime = starttime['trade_date__max']
    # 如果未获取最大日期值，默认为_STARTTIME
    if (starttime is None): starttime = _STARTTIME
    _NOWTIME = get_nowtime()  # 需要重新获取本地时间

    if starttime >= _NOWTIME:
        return ('资金流向信息更新至' + starttime + '\n')

    try:
        engine = settings.DATABASES['default']['NAME']
        cn = sqlite3.connect(engine)
    except:
        print('创建数据连接失败')
        return

    begin = _str_time_to_time(starttime)  # 转换成日期格式%Y-%m-%d
    end = _str_time_to_time(_NOWTIME)
    labelstr = ''
    for i in range(1, (end - begin).days + 1):  # 按日期循环
        day = begin + datetime.timedelta(days=i)
        if (datetime.date.isoweekday(day) == 6) or (datetime.date.isoweekday(day) == 7): continue  # 周末数据不加载
        day = str(day).replace('-', '')
        labelstr += _get_one_day_moneyflow_info(day, cn) + '\n'
        time.sleep(60 / 200)
    return labelstr


# 获取某天涨跌停统计
def _get_one_day_limit_list(endtimetmp, cn):
    try:
        df = tspro.limit_list(trade_date=endtimetmp)
    except:
        return ('获取网络数据失败，请检查网络是否连接')

    if len(df.values) == 0:
        return endtimetmp + '涨跌停数据暂未更新'  # 无数据则推出函数
    try:
        df.to_sql(LIMIT_LIST_TABLE, cn, index=False, if_exists='append')
        return ('插入' + str(endtimetmp) + '涨跌停信息共%s条' % (len(df.values)))
    except:
        return ('插入' + str(endtimetmp) + '涨跌停信息失败,请检查数据库是否开启！')


def get_all_limit_list():
    starttime = models.limit_list.objects.all().aggregate(Max('trade_date'))
    starttime = starttime['trade_date__max']
    # 如果未获取最大日期值，默认为_STARTTIME
    if (starttime is None): starttime = _STARTTIME
    _NOWTIME = get_nowtime()  # 需要重新获取本地时间

    if starttime >= _NOWTIME:
        return ('每日涨跌停信息更新至' + starttime + '\n')

    try:
        engine = settings.DATABASES['default']['NAME']
        cn = sqlite3.connect(engine)
    except:
        print('创建数据连接失败')
        return

    begin = _str_time_to_time(starttime)  # 转换成日期格式%Y-%m-%d
    end = _str_time_to_time(_NOWTIME)
    labelstr = ''
    for i in range(1, (end - begin).days + 1):  # 按日期循环
        day = begin + datetime.timedelta(days=i)
        if (datetime.date.isoweekday(day) == 6) or (datetime.date.isoweekday(day) == 7): continue  # 周末数据不加载
        day = str(day).replace('-', '')
        labelstr += _get_one_day_limit_list(day, cn) + '\n'
        time.sleep(60 / 200)
    return labelstr


# 获取股东公告
def _get_stk_holdernumber(endtimetmp, cn):
    try:
        df = tspro.stk_holdernumber(start_date=endtimetmp)
    except:
        return ('获取网络数据失败，请检查网络是否连接')

    if len(df.values) == 0:
        return endtimetmp + '股东人数数据暂未更新'  # 无数据则推出函数
    try:
        df.to_sql(STK_HOLDERNUMBER_TABLE, cn, index=False, if_exists='append')
        return ('插入' + str(endtimetmp) + '股东公告信息共%s条' % (len(df.values)))
    except:
        return ('插入' + str(endtimetmp) + '股东公告信息失败,请检查数据库是否开启！')


def get_all_stk_holdernumber():
    starttime = models.stk_holdernumber.objects.all().aggregate(Max('ann_date'))
    starttime = starttime['ann_date__max']
    # 如果未获取最大日期值，默认为_STARTTIME
    if (starttime is None): starttime = '20190630'
    _NOWTIME = get_nowtime()  # 需要重新获取本地时间

    if starttime >= _NOWTIME:
        return ('股东公告信息更新至' + starttime + '\n')

    try:
        engine = settings.DATABASES['default']['NAME']
        cn = sqlite3.connect(engine)
    except:
        print('创建数据连接失败')
        return

    begin = _str_time_to_time(starttime)  # 转换成日期格式%Y-%m-%d

    labelstr = ''

    day = begin + datetime.timedelta(days=1)

    day = str(day).replace('-', '')
    labelstr += _get_stk_holdernumber(day, cn) + '\n'
    time.sleep(60 / 200)
    return labelstr


def get_concept():
    """
    获取概念股列表
    :return:
    """
    try:
        engine = settings.DATABASES['default']['NAME']
        cn = sqlite3.connect(engine)
    except:
        print('创建数据连接失败')
        return

    try:
        df = tspro.concept()
    except:
        return ('获取网络数据失败，请检查网络是否连接')

    if len(df.values) == 0:
        return '概念股数据为0'  # 无数据则推出函数
    try:
        # pd.DataFrame(df).rename(columns={'id': 'code'})
        models.concept.objects.all().delete()
        df.to_sql(CONCEPT_TABLE, cn, index=False, if_exists='append')
        return ('插入概念股分类信息共%s条' % (len(df.values)))
    except:
        return ('插入概念股分类信息信息失败,请检查数据库是否开启！')


def update_all_concept_detail():
    """
    获取概念股详细列表
    :return:
    """
    try:
        engine = settings.DATABASES['default']['NAME']
        cn = sqlite3.connect(engine)
    except:
        print('创建数据连接失败')
        return

    try:
        concept_df = models.concept.objects.all()
    except:
        return '概念股列表获取失败'

    for concept in concept_df:
        try:
            df = tspro.concept_detail(id=concept.code)
        except:
            return ('获取网络数据失败，请检查网络是否连接')

        if len(df.values) == 0:
            return concept.name + '  ：概念股票数为0'  # 无数据则推出函数
        try:
            df = pd.DataFrame(df).rename(columns={'id': 'code'})
            models.concept_detail.objects.filter(code=concept.code).delete()
            df.to_sql(CONCEPT_DETAIL_TABLE, cn, index=False, if_exists='append')

            # print(concept.name + ' ：概念股信息共%s条' % (len(df.values)))
        except:
            return ('概念股详细信息失败,请检查数据库是否开启！')


def get_all_info():
    a = get_all_limit_list() + '\n'
    a = a + get_all_moneyflow_info() + '\n'
    a = a + get_all_block_trade_info() + '\n'
    a = a + get_all_stock_daily_basic_info() + '\n'
    a = a + get_all_stock_daily_info() + '\n'
    return a
